package com.atguigu.ssyx.activity.service.impl;

import com.atguigu.ssyx.activity.mapper.ActivityRuleMapper;
import com.atguigu.ssyx.activity.mapper.ActivitySkuMapper;
import com.atguigu.ssyx.activity.service.CouponInfoService;
import com.atguigu.ssyx.client.product.ProductFeignClient;
import com.atguigu.ssyx.enums.ActivityType;
import com.atguigu.ssyx.model.activity.ActivityInfo;
import com.atguigu.ssyx.activity.mapper.ActivityInfoMapper;
import com.atguigu.ssyx.activity.service.ActivityInfoService;
import com.atguigu.ssyx.model.activity.ActivityRule;
import com.atguigu.ssyx.model.activity.ActivitySku;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.activity.ActivityRuleVo;
import com.atguigu.ssyx.vo.order.CartInfoVo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 活动表 服务实现类
 * </p>
 *
 * @author lqh
 * @since 2023-09-26
 */
@Service
public class ActivityInfoServiceImpl extends ServiceImpl<ActivityInfoMapper, ActivityInfo> implements ActivityInfoService {

    //规则
    @Autowired
    private ActivityRuleMapper activityRuleMapper;

    //商品
    @Autowired
    private ActivitySkuMapper activitySkuMapper;

    @Autowired
    private ProductFeignClient productFeignClient;
    //@EnableFeignClients
    //启动类要加这个注解

    //优惠劵
    @Autowired
    private CouponInfoService couponInfoService;

    @Override
    public void removeByIdAndActivitySkuId(Long id) {
        //删除活动列表的同时 删除activity_sku中的sku信息
        baseMapper.deleteById(id);
        //activity_sku
        LambdaQueryWrapper<ActivitySku> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ActivitySku::getActivityId,id);
        activitySkuMapper.delete(wrapper);
        //activity_rule
        LambdaQueryWrapper<ActivityRule> wrapperRule = new LambdaQueryWrapper<>();
        wrapperRule.eq(ActivityRule::getActivityId,id);
        activityRuleMapper.delete(wrapperRule);
    }

    //分页查询
    @Override
    public IPage<ActivityInfo> selectPage(Page<ActivityInfo> pageParam) {
        IPage<ActivityInfo> activityInfoPage = baseMapper.selectPage(pageParam, null);
        //从对象获取数据 分页查询中 获取当前页的数据-当前页的数据
        List<ActivityInfo> activityInfos = activityInfoPage.getRecords();
        //细节问题 从枚举中获取数据类型 放到属性中去
        //集合 遍历
        //向对象中封装类型到对象中去
        activityInfos.stream().forEach(item -> {
            //之前存储的是枚举
            item.setActivityTypeString(item.getActivityType().getComment());
        });
        return activityInfoPage;
    }

    //根据Id获取活动数据
    @Override
    public Map<String, Object> findActivityRuleList(Long id) {
        //返回的是Map集合
        Map<String, Object> result = new HashMap<>();

        //根据活动id查询 查询规则列表activity-rule表
        LambdaQueryWrapper<ActivityRule> wrapperRole = new LambdaQueryWrapper<>();
        wrapperRole.eq(ActivityRule::getActivityId, id);
        List<ActivityRule> activityRules = activityRuleMapper.selectList(wrapperRole);
        result.put("activityRuleList", activityRules);

        //根据活动id查询 查询使用规则商品列表 activity_sku
        //activity_sku表中suk的信息 值存储了skuID 所以还要通过远程调用来查询sku的信息
        List<ActivitySku> activitySkus = activitySkuMapper.selectList(
                new LambdaQueryWrapper<ActivitySku>().eq(ActivitySku::getActivityId, id)
        ); //然后得到所有的skuId 放到新的集合中去
        List<Long> skuIdList =
                activitySkus.stream().map(ActivitySku::getSkuId).collect(Collectors.toList());
        //但是sku信息在别的数据库中 现在要使用远程调用来实现
        //通过远程调用service-product模块接口 根据skuId得到商品信息
        //存在skuIdList为空的情况 那么在远程调用反序列化空值 出错
        List<SkuInfo> skuInfoList = null;
        if (!skuIdList.isEmpty()){
            //出现空值的问题
            skuInfoList = productFeignClient.findSkuInfoList(skuIdList);
        }
        //远程调用 得到
        result.put("skuInfoList", skuInfoList);
        return result;
    }

    //在活动中添加规则数据
    @Override
    public void saveActivityRule(ActivityRuleVo activityRuleVo) {
        //两张表 a_rule,a_sku
        //第一步 根据活动Id 删除之前规则数据
        activityRuleMapper.delete(
                new LambdaQueryWrapper<ActivityRule>().eq(ActivityRule::getActivityId,
                        activityRuleVo.getActivityId())
        );
        //在·删·SKu
        activitySkuMapper.delete(
                new LambdaQueryWrapper<ActivitySku>().eq(ActivitySku::getActivityId,
                        activityRuleVo.getActivityId())
        );

        //添加

        //获取规则列表数据
        ActivityInfo activityInfo = baseMapper.selectById(activityRuleVo.getActivityId());
        List<ActivityRule> activityRuleList = activityRuleVo.getActivityRuleList();
        //表activity_rule
        for (ActivityRule activityRule : activityRuleList) {
            //单独封装下面两个字段 前端没有传过来 但是数据库中有这个两个字段
            //Id和类型单独封装
            activityRule.setActivityId(activityRuleVo.getActivityId());
            //主要就是从activity中获取类型
            activityRule.setActivityType(activityInfo.getActivityType());
            activityRuleMapper.insert(activityRule);
        }

        //获取规则范围数据 表activity_sku
        List<ActivitySku> activitySkuList = activityRuleVo.getActivitySkuList();
        for (ActivitySku activitySku : activitySkuList) {
            activitySku.setActivityId(activityRuleVo.getActivityId());
            activitySkuMapper.insert(activitySku);
        }

    }

    //根据关键字查询匹配Sku信息
    @Override
    public List<SkuInfo> findSkuInfoKeyWord(String keyword) {
        //这个keyword 是根据product数据库中 skuInfo表中的sku_name字段进行匹配

        //第一步 根据输入的关键字 sku 匹配内容列表
        //1.当前在的模块是service-activity 而Sku是在product部分
        //所以要远程调用
        //2.service-product模块创建接口 根据关键字sku匹配内容列表
        //service-activity远程调用得到sku内容列表
        List<SkuInfo> skuInfoList = productFeignClient.findSkuInfoByKeyword(keyword);
        //如果根据关键字查询不到匹配内容 直接返回空集合
        if (skuInfoList.size() == 0) {
            return skuInfoList;
        }

        //从skuInfo里面 获取所有的SkuId
        List<Long> skuIdList =
                skuInfoList.stream().map(SkuInfo::getId).collect(Collectors.toList());

        //第二步 判断添加商品之前是否参加过活动 如果活动之前参加过 活动正在进行中 那么就排除商品
        //1.第一步 查寻两个找 做判断 一个是activity-info 一个是activity-sku 编写SQL语句实现
        //拿着activity_info中的activity_id去activity_rule表中比对重复存在的skuId 就是一个skuId只能对应一个activity_id
        //查出来的是存在的商品的skuId
        List<Long> exitSkuIdList = baseMapper.selectSkuIdListExist(skuIdList);
        //判断逻辑处理 排除已经参加活动商品
        List<SkuInfo> findSkuList = new ArrayList<>();
        //遍历全部的skuInfo列表
        for (SkuInfo skuInfo : skuInfoList) {
            //判断这个Id在里面是不否存在
            if (!exitSkuIdList.contains(skuInfo.getId())) {
                //不存在就封装
                findSkuList.add(skuInfo);
            }
        }
        return findSkuList;
    }

    //根据skuId列表促销信息
    @Override
    public Map<Long, List<String>> findActivity(List<Long> skuIdList) {
        Map<Long, List<String>> result = new HashMap<>();
        //skuIdList遍历 得到每个skuId
        skuIdList.forEach(skuId -> {
            List<ActivityRule> activityRuleList =
                    //三张表的查询
                    //activity_info 查询活动返回是在当前的时间之内
                    //activity_rule
                    //activity_sku
                    baseMapper.findActivityRule(skuId);

            if (!CollectionUtils.isEmpty(activityRuleList)) {

                List<String> ruleList = new ArrayList<>();
                for (ActivityRule activityRule : activityRuleList) {
                    ruleList.add(this.getRuleDesc(activityRule));
                }
                result.put(skuId, ruleList);
            }
        });
        //根据skuId进行查询 查询sku对应活动里面规则列表
        //数据封装 规则名称
        return result;
    }

    //构造规则名称的方法
    private String getRuleDesc(ActivityRule activityRule) {
        ActivityType activityType = activityRule.getActivityType();
        StringBuffer ruleDesc = new StringBuffer();
        if (activityType == ActivityType.FULL_REDUCTION) {
            ruleDesc
                    .append("满")
                    .append(activityRule.getConditionAmount())
                    .append("元减")
                    .append(activityRule.getBenefitAmount())
                    .append("元");
        } else {
            ruleDesc
                    .append("满")
                    .append(activityRule.getConditionNum())
                    .append("元打")
                    .append(activityRule.getBenefitDiscount())
                    .append("折");
        }
        return ruleDesc.toString();
    }

    //根据skuId获取营销数据和优惠卷
    @Override
    public Map<String, Object> findActivityAndCoupon(Long skuId, Long userId) {
        //根据SkuId获取sku营销活动 一个活动有多个规则
        Map<String, Object> activityRuleList = this.findActivityRuleList(skuId);

        List<CouponInfo> couponInfoList = couponInfoService.findCouponInfoList(skuId, userId);

        //封装到Map集合 返回
        Map<String, Object> map = new HashMap<>();
        map.put("couponInfoList", couponInfoList);
        map.putAll(activityRuleList);
        return map;
    }

    //获取购物车里面满足条件优惠卷 和 活动的信息
    @Override
    public OrderConfirmVo findCartActivityAndCoupon(List<CartInfo> cartInfoList,
                                                    //整个List<CartInfo>是个购物车
                                                    //每个cartInfo就是一个购物项
                                                    Long userId) {
        //1.获取购物车 每个购物项参与活动 根据活动规则进行分组
        //一个活动规则对应多个商品
        //CartInfoVo
        List<CartInfoVo> cartInfoVoList = this.findCartActivityList(cartInfoList);

        //2.计算商品参与活动 最后的商品是多少
        BigDecimal activityReduceAmount = cartInfoVoList.stream()
                .filter(cartInfoVo -> cartInfoVo.getActivityRule() != null)
                .map(cartInfoVo -> cartInfoVo.getActivityRule().getReduceAmount())
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        //3.获取购物车可以使用的优惠卷列表
        List<CouponInfo> couponInfoList = couponInfoService.findCartCouponInfo(cartInfoList,userId);

        //4.计算商品使用优惠卷之后金额 一次只能使用一张优惠卷
        BigDecimal couponReduceAmount = new BigDecimal(0);
        if (!CollectionUtils.isEmpty(couponInfoList)) {
            couponInfoList.stream()
                    .filter(couponInfo -> couponInfo.getIsOptimal().intValue() == 1)
                    .map(couponInfo -> couponInfo.getAmount())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        //5.没有参与活动 没有使用优惠换原始金额
        BigDecimal originalTotalAmount = cartInfoList.stream()
                .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                .map(cartInfo -> cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        //6.参与活动 也使用优惠卷总金额 最终金额
        BigDecimal totalAmount =
                originalTotalAmount.subtract(activityReduceAmount).subtract(couponReduceAmount);

        //7.封装需要数据到
        OrderConfirmVo orderTradeVo = new OrderConfirmVo();
        orderTradeVo.setCarInfoVoList(cartInfoVoList);
        orderTradeVo.setActivityReduceAmount(activityReduceAmount);
        orderTradeVo.setCouponInfoList(couponInfoList);
        orderTradeVo.setCouponReduceAmount(couponReduceAmount);
        orderTradeVo.setOriginalTotalAmount(originalTotalAmount);
        orderTradeVo.setTotalAmount(totalAmount);

        return orderTradeVo;
    }

    //获取购物车对应规则数据
    @Override
    public List<CartInfoVo> findCartActivityList(List<CartInfo> cartInfoList) {
        //创建最终返回的集合
        List<CartInfoVo> cartInfoVoList = new ArrayList<>();
        //第一部分 获取所有skuId
        List<Long> skuIdList =
                cartInfoList.stream().map(CartInfo::getSkuId).collect(Collectors.toList());

        //根据所有的skuId获取参与活动
        List<ActivitySku> activitySkuList = baseMapper.selectCartActivity(skuIdList);

        //对查出来的数据 规划 分组处理 每个活动里面有哪些skuId信息
        //Map里面key是分组字段 活动Id
        //value是每组里面sku列表数据 set集合
        Map<Long, Set<Long>> activityIdToSkuIdListMap = activitySkuList.stream()
                .collect(
                        Collectors.groupingBy(
                                ActivitySku::getActivityId,
                                Collectors.mapping(ActivitySku::getSkuId,
                                        Collectors.toSet())
                        )
                );

        //获取活动里面规则数据
        //key是活动Id  value是活动里面规则列表数据
        Map<Long, List<ActivityRule>> activityIdToActivityRuleListMap
                = new HashMap<>();
        //所有活动Id
        Set<Long> activityIdSet = activitySkuList.stream().map(ActivitySku::getActivityId)
                .collect(Collectors.toSet());

        if (!CollectionUtils.isEmpty(activityIdSet)) {
            //activity_rule表
            LambdaQueryWrapper<ActivityRule> wrapper = new LambdaQueryWrapper<>();
            //排序 满减金额
            wrapper.orderByDesc(ActivityRule::getConditionAmount, ActivityRule::getConditionNum);

            //in 查询范围的值
            wrapper.in(ActivityRule::getActivityId, activityIdSet);
            List<ActivityRule> activityRuleList = activityRuleMapper.selectList(wrapper);
            //封装到activityIdToActivityRuleListMap里面
            //根据活动id进行分组
            activityIdToActivityRuleListMap =
                    activityRuleList.stream().collect(Collectors.groupingBy(activityRule -> activityRule.getActivityId()));


        }

        //有活动的购物项skuId
        Set<Long> activitySkuIdSet = new HashSet<>();
        if (!CollectionUtils.isEmpty(activityIdToSkuIdListMap)) {
            //遍历Map集合  使用迭代器
            Iterator<Map.Entry<Long, Set<Long>>> iterator =
                    activityIdToSkuIdListMap.entrySet().iterator();

            while (iterator.hasNext()) {
                Map.Entry<Long, Set<Long>> next = iterator.next();
                //活动Id
                Long activityId = next.getKey();
                //每个活动对应skuId列表
                Set<Long> currentActivitySkuIdSet = next.getValue();

                //获取当前活动购物项的列表
                List<CartInfo> currentActivityCartInfoList =
                        cartInfoList.stream().filter(cartInfo
                                -> currentActivitySkuIdSet.contains(cartInfo.getSkuId())).collect(Collectors.toList());

                //计算购物项总的金额和数量
                //金额
                BigDecimal activityTotalAmount =
                        this.computeTotalAmount(currentActivityCartInfoList);
                //数量
                int activityTotalNum = this.computeCartNum(currentActivityCartInfoList);

                //计算活动对应规则
                //根据activityId获取活动对应规则
                List<ActivityRule> currentActivityRuleList =
                        activityIdToActivityRuleListMap.get(activityId);

                ActivityType activityType =
                        currentActivityRuleList.get(0).getActivityType();

                //判断活动类型 是满减还是打折
                ActivityRule activityRule = null;
                if (activityType == ActivityType.FULL_REDUCTION) {
                    //满减
                    activityRule = this.computeFullReduction(activityTotalAmount, currentActivityRuleList);
                } else {
                    //满量
                    activityRule =
                            this.computeFullDiscount(activityTotalNum, activityTotalAmount, currentActivityRuleList);
                }
                //封装CartInfoVo封装
                CartInfoVo cartInfoVo = new CartInfoVo();
                cartInfoVo.setActivityRule(activityRule);
                cartInfoVo.setCartInfoList(currentActivityCartInfoList);
                cartInfoVoList.add(cartInfoVo);

                //记录哪些购物项参与活动
                activitySkuIdSet.addAll(currentActivitySkuIdSet);
            }
        }
        //没有活动购物项skuId
        //获取哪些skuId没有参加活动 这个方法是移除了参加的购物项
        skuIdList.removeAll(activityIdSet);
        if (!CollectionUtils.isEmpty(skuIdList)) {
            //skuId对应购物项
            Map<Long, CartInfo> skuIdCartInfoMap =
                    cartInfoList.stream().collect(Collectors.toMap(CartInfo::getSkuId, CartInfo -> CartInfo));

            for (Long skuId : skuIdList) {
                CartInfoVo cartInfoVo = new CartInfoVo();
                cartInfoVo.setActivityRule(null);
                //没有活动
                List<CartInfo> cartInfos = new ArrayList<>();
                cartInfos.add(skuIdCartInfoMap.get(skuId));
                cartInfoVo.setCartInfoList(cartInfos);
                cartInfoVoList.add(cartInfoVo);
            }
        }
        return cartInfoVoList;
    }

    /**
     * 计算满量打折最优规则
     *
     * @param totalNum
     * @param activityRuleList //该活动规则skuActivityRuleList数据，已经按照优惠折扣从大到小排序了
     */
    private ActivityRule computeFullDiscount(Integer totalNum, BigDecimal totalAmount, List<ActivityRule> activityRuleList) {
        ActivityRule optimalActivityRule = null;
        //该活动规则skuActivityRuleList数据，已经按照优惠金额从大到小排序了
        for (ActivityRule activityRule : activityRuleList) {
            //如果订单项购买个数大于等于满减件数，则优化打折
            if (totalNum.intValue() >= activityRule.getConditionNum()) {
                BigDecimal skuDiscountTotalAmount = totalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
                BigDecimal reduceAmount = totalAmount.subtract(skuDiscountTotalAmount);
                activityRule.setReduceAmount(reduceAmount);
                optimalActivityRule = activityRule;
                break;
            }
        }
        if (null == optimalActivityRule) {
            //如果没有满足条件的取最小满足条件的一项
            optimalActivityRule = activityRuleList.get(activityRuleList.size() - 1);
            optimalActivityRule.setReduceAmount(new BigDecimal("0"));
            optimalActivityRule.setSelectType(1);

            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionNum())
                    .append("元打")
                    .append(optimalActivityRule.getBenefitDiscount())
                    .append("折，还差")
                    .append(totalNum - optimalActivityRule.getConditionNum())
                    .append("件");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
        } else {
            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionNum())
                    .append("元打")
                    .append(optimalActivityRule.getBenefitDiscount())
                    .append("折，已减")
                    .append(optimalActivityRule.getReduceAmount())
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
            optimalActivityRule.setSelectType(2);
        }
        return optimalActivityRule;
    }

    /**
     * 计算满减最优规则
     *
     * @param totalAmount
     * @param activityRuleList //该活动规则skuActivityRuleList数据，已经按照优惠金额从大到小排序了
     */
    private ActivityRule computeFullReduction(BigDecimal totalAmount, List<ActivityRule> activityRuleList) {
        ActivityRule optimalActivityRule = null;
        //该活动规则skuActivityRuleList数据，已经按照优惠金额从大到小排序了
        for (ActivityRule activityRule : activityRuleList) {
            //如果订单项金额大于等于满减金额，则优惠金额
            if (totalAmount.compareTo(activityRule.getConditionAmount()) > -1) {
                //优惠后减少金额
                activityRule.setReduceAmount(activityRule.getBenefitAmount());
                optimalActivityRule = activityRule;
                break;
            }
        }
        if (null == optimalActivityRule) {
            //如果没有满足条件的取最小满足条件的一项
            optimalActivityRule = activityRuleList.get(activityRuleList.size() - 1);
            optimalActivityRule.setReduceAmount(new BigDecimal("0"));
            optimalActivityRule.setSelectType(1);

            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionAmount())
                    .append("元减")
                    .append(optimalActivityRule.getBenefitAmount())
                    .append("元，还差")
                    .append(totalAmount.subtract(optimalActivityRule.getConditionAmount()))
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
        } else {
            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionAmount())
                    .append("元减")
                    .append(optimalActivityRule.getBenefitAmount())
                    .append("元，已减")
                    .append(optimalActivityRule.getReduceAmount())
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
            optimalActivityRule.setSelectType(2);
        }
        return optimalActivityRule;
    }

    private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
        BigDecimal total = new BigDecimal("0");
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if (cartInfo.getIsChecked().intValue() == 1) {
                BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                total = total.add(itemTotal);
            }
        }
        return total;
    }

    private int computeCartNum(List<CartInfo> cartInfoList) {
        int total = 0;
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if (cartInfo.getIsChecked().intValue() == 1) {
                total += cartInfo.getSkuNum();
            }
        }
        return total;
    }
}
